﻿using System;
using System.Linq;

namespace ArduinoInterOp
{
    //|command (8)|sensor (8) | value (10)|NONE (6); Identifier (32)
    // ********   - ********  -********** - ****** ; ********-********-********-********

    /*
     * Command examples:
     * Get sensor 1's value:
     * 00000001 00000001 0000000000 000000 ; 00000000 00000000 00000000 00000000 
     * 
     * Set sensor 13's value to 1:
     * 
     * 00000010 00001101 0000000001 000000 ; 00000000 00000000 00000000 00000000 (34406464;0)
     */

    public abstract class ArduinoCommand
    {
        private long _startTimeInTicks;
        private DateTime _startTime;

        protected ArduinoCommand(params ArduinoPinOperationData[] operations)
        {
            Operations = operations;
            _startTimeInTicks = 0;
        }

        public DateTime StartTime
        {
            get { return _startTime; }
            set
            {
                if (DateTime.Now <= value)
                {
                    _startTime = value;
                }
                else
                {
                    throw new ArgumentOutOfRangeException(paramName: "StartTimeInTicks");
                }
            }
        }

        internal long StartTimeInTicks
        {
            get { return _startTime.Ticks; }
        }

        internal Arduino ArduinoHandler { get; set; }

        protected ushort? OutputPinValue(byte pinNumber)
        {
            return ArduinoHandler.PinsManager.OutputPins[pinNumber].Value;
        }

        protected ushort? LastKnownInputValue(byte pinNumber)
        {
            return ArduinoHandler.PinsManager.InputPins[pinNumber].Value;
        }

        protected internal ArduinoPinOperationData[] Operations { get; set; }

        protected internal virtual void OnBeforeExecuting() { }

        internal string ToArduinoCommandsString()
        {
            return Operations.Aggregate(string.Empty,
                                        (current, operation) =>
                                        current + (operation.ToNumericRepresentationString() + "-"));
        }

        internal void ProcessResultInternal()
        {
            switch (ProcessResult())
            {
                case ArduinoCommandResult.Finished:
                this.State = ArduinoCommandState.Finished;
                    break;
                case ArduinoCommandResult.Continue:
                this.State = ArduinoCommandState.Pending;
                    break;
                case ArduinoCommandResult.Stopped:
                this.State = ArduinoCommandState.Stopped;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        protected virtual ArduinoCommandResult ProcessResult()
        {
            this.Operations = null;
            return ArduinoCommandResult.Finished;
        }

        internal bool Validate(ArduinoSetup setup)
        {
            return true;
        }

        public ArduinoCommandState State { get; internal set; }
    }
}